using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace FPLibrary
{
  /// <summary>
  /// A fixed-point number. 16 lower bits are used for the decimal part, 48 for the integral part.
  /// </summary>
  /// 
  ///             However, majority of internal code and the multiplication operator perform fast multiplication,
  ///             where the result can use at most 32 bits for the integral part and overflows are not detected.
  ///             This means that you should stay in <see cref="T:System.Int16" />
  ///  range.
  ///             <seealso cref="P:Photon.Deterministic.FP.UseableMax" />
  /// <seealso cref="P:Photon.Deterministic.FP.UseableMin" />
  /// 
  ///             \ingroup MathAPI
  [Serializable]
  [StructLayout(LayoutKind.Explicit)]
  public struct FP : IEquatable<FP>, IComparable<FP>
  {
    public const int SIZE = 8;
    private const int FRACTIONS_COUNT = 5;
    public const long RAW_ONE = 65536;
    public const long RAW_ZERO = 0;
    public const int Precision = 16;
    public const int Bits = 64;
    public const long MulRound = 0;
    public const int MulShift = 16;
    public const int MulShiftTrunc = 16;
    internal const bool UsesRoundedConstants = false;
    [FieldOffset(0)]
    public long RawValue;

    /// <summary>Closest double: 1.52587890625E-05</summary>
    public static unsafe FP SmallestNonZero
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &1L;
    }

    /// <summary>Closest double: -140737488355328</summary>
    public static unsafe FP MinValue
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &long.MinValue;
    }

    /// <summary>Closest double: 140737488355328</summary>
    public static unsafe FP MaxValue
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &long.MaxValue;
    }

    /// <summary>
    /// ‭All values between <see cref="P:Photon.Deterministic.FP.UseableMin" /> and <see cref="P:Photon.Deterministic.FP.UseableMax" /> (inclusive) are guaranteed not
    /// to overflow when multiplicated.
    /// </summary>
    /// <summary>Closest double: -32768</summary>
    public static unsafe FP UseableMin
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &(long) int.MinValue;
    }

    /// <summary>
    /// All values between <see cref="P:Photon.Deterministic.FP.UseableMin" /> and <see cref="P:Photon.Deterministic.FP.UseableMax" /> (inclusive) are guaranteed not
    /// to overflow when multiplicated.
    /// </summary>
    /// <summary>Closest double: 32767.9999847412</summary>
    public static unsafe FP UseableMax
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &(long) int.MaxValue;
    }

    /// <summary>Pi number.</summary>
    /// <summary>Closest double: 3.14158630371094</summary>
    public static unsafe FP Pi
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &205887L;
    }

    /// <summary>1/Pi.</summary>
    /// <summary>Closest double: 0.31829833984375</summary>
    public static unsafe FP PiInv
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &20860L;
    }

    /// <summary>2 * Pi.</summary>
    /// <summary>Closest double: 6.28317260742188</summary>
    public static unsafe FP PiTimes2
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &411774L;
    }

    /// <summary>Pi / 2.</summary>
    /// <summary>Closest double: 1.57078552246094</summary>
    public static unsafe FP PiOver2
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &102943L;
    }

    /// <summary>2 / Pi.</summary>
    /// <summary>Closest double: 0.636611938476563</summary>
    public static unsafe FP PiOver2Inv
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &41721L;
    }

    /// <summary>Pi / 4.</summary>
    /// <summary>Closest double: 0.785385131835938</summary>
    public static unsafe FP PiOver4
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &51471L;
    }

    /// <summary>3 * Pi / 4.</summary>
    /// <summary>Closest double: 2.35618591308594</summary>
    public static unsafe FP Pi3Over4
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &154415L;
    }

    /// <summary>Closest double: 0.0174407958984375</summary>
    public static unsafe FP Deg2Rad
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &1143L;
    }

    /// <summary>Closest double: 57.2957763671875</summary>
    public static unsafe FP Rad2Deg
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &3754936L;
    }

    /// <summary>Closest double: 0</summary>
    public static unsafe FP _0
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &0L;
    }

    /// <summary>Closest double: 1</summary>
    public static unsafe FP _1
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &65536L;
    }

    /// <summary>Closest double: 2</summary>
    public static unsafe FP _2
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &131072L;
    }

    /// <summary>Closest double: 3</summary>
    public static unsafe FP _3
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &196608L;
    }

    /// <summary>Closest double: 4</summary>
    public static unsafe FP _4
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &262144L;
    }

    /// <summary>Closest double: 5</summary>
    public static unsafe FP _5
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &327680L;
    }

    /// <summary>Closest double: 6</summary>
    public static unsafe FP _6
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &393216L;
    }

    /// <summary>Closest double: 7</summary>
    public static unsafe FP _7
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &458752L;
    }

    /// <summary>Closest double: 8</summary>
    public static unsafe FP _8
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &524288L;
    }

    /// <summary>Closest double: 9</summary>
    public static unsafe FP _9
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &589824L;
    }

    /// <summary>Closest double: 10</summary>
    public static unsafe FP _10
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &655360L;
    }

    /// <summary>Closest double: 99</summary>
    public static unsafe FP _99
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &6488064L;
    }

    /// <summary>Closest double: 100</summary>
    public static unsafe FP _100
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &6553600L;
    }

    /// <summary>Closest double: 200</summary>
    public static unsafe FP _200
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &13107200L;
    }

    /// <summary>Closest double: 1000</summary>
    public static unsafe FP _1000
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &65536000L;
    }

    /// <summary>Closest double: 10000</summary>
    public static unsafe FP _10000
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &655360000L;
    }

    /// <summary>Closest double: 0.0099945068359375</summary>
    public static unsafe FP _0_01
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &655L;
    }

    /// <summary>Closest double: 0.019989013671875</summary>
    public static unsafe FP _0_02
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &1310L;
    }

    /// <summary>Closest double: 0.0999908447265625</summary>
    public static unsafe FP _0_10
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &6553L;
    }

    /// <summary>Closest double: 0.199996948242188</summary>
    public static unsafe FP _0_20
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &13107L;
    }

    /// <summary>Closest double: 0.25</summary>
    public static unsafe FP _0_25
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &16384L;
    }

    /// <summary>Closest double: 0.5</summary>
    public static unsafe FP _0_50
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &32768L;
    }

    /// <summary>Closest double: 0.75</summary>
    public static unsafe FP _0_75
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &49152L;
    }

    /// <summary>Closest double: 0.0299835205078125</summary>
    public static unsafe FP _0_03
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &1965L;
    }

    /// <summary>Closest double: 0.03997802734375</summary>
    public static unsafe FP _0_04
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &2620L;
    }

    /// <summary>Closest double: 0.0499725341796875</summary>
    public static unsafe FP _0_05
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &3275L;
    }

    /// <summary>Closest double: 0.333328247070313</summary>
    public static unsafe FP _0_33
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &21845L;
    }

    /// <summary>Closest double: 0.990005493164063</summary>
    public static unsafe FP _0_99
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &64881L;
    }

    /// <summary>Closest double: -1</summary>
    public static unsafe FP Minus_1
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &-65536L;
    }

    /// <summary>Closest double: 3.14158630371094</summary>
    public static unsafe FP Rad_180
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &205887L;
    }

    /// <summary>Closest double: 1.57078552246094</summary>
    public static unsafe FP Rad_90
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &102943L;
    }

    /// <summary>Closest double: 0.785385131835938</summary>
    public static unsafe FP Rad_45
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &51471L;
    }

    /// <summary>Closest double: 0.392684936523438</summary>
    public static unsafe FP Rad_22_50
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &25735L;
    }

    /// <summary>Closest double: 1.00999450683594</summary>
    public static unsafe FP _1_01
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &66191L;
    }

    /// <summary>Closest double: 1.01998901367188</summary>
    public static unsafe FP _1_02
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &66846L;
    }

    /// <summary>Closest double: 1.02999877929688</summary>
    public static unsafe FP _1_03
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &67502L;
    }

    /// <summary>Closest double: 1.03999328613281</summary>
    public static unsafe FP _1_04
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &68157L;
    }

    /// <summary>Closest double: 1.04998779296875</summary>
    public static unsafe FP _1_05
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &68812L;
    }

    /// <summary>Closest double: 1.09999084472656</summary>
    public static unsafe FP _1_10
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &72089L;
    }

    /// <summary>Closest double: 1.19999694824219</summary>
    public static unsafe FP _1_20
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &78643L;
    }

    /// <summary>Closest double: 1.25</summary>
    public static unsafe FP _1_25
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &81920L;
    }

    /// <summary>Closest double: 1.5</summary>
    public static unsafe FP _1_50
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &98304L;
    }

    /// <summary>Closest double: 1.75</summary>
    public static unsafe FP _1_75
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &114688L;
    }

    /// <summary>Closest double: 1.33332824707031</summary>
    public static unsafe FP _1_33
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &87381L;
    }

    /// <summary>Closest double: 1.99000549316406</summary>
    public static unsafe FP _1_99
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &130417L;
    }

    /// <summary>Closest double: 0.0999908447265625</summary>
    public static unsafe FP EN1
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &6553L;
    }

    /// <summary>Closest double: 0.0099945068359375</summary>
    public static unsafe FP EN2
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &655L;
    }

    /// <summary>Closest double: 0.0009918212890625</summary>
    public static unsafe FP EN3
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &65L;
    }

    /// <summary>Closest double: 9.1552734375E-05</summary>
    public static unsafe FP EN4
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &6L;
    }

    /// <summary>Closest double: 0</summary>
    public static unsafe FP EN5
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &0L;
    }

    /// <summary>Closest double: 0.0009918212890625</summary>
    public static unsafe FP Epsilon
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &65L;
    }

    /// <summary>Closest double: 2.71827697753906</summary>
    public static unsafe FP E
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &178145L;
    }

    /// <summary>Closest double: 1.44268798828125</summary>
    public static unsafe FP Log2_E
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &94548L;
    }

    /// <summary>Closest double: 3.32191467285156</summary>
    public static unsafe FP Log2_10
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => *(FP*) &217705L;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator -(FP a)
    {
      a.RawValue = -a.RawValue;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator +(FP a)
    {
      a.RawValue = a.RawValue;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator +(FP a, FP b)
    {
      a.RawValue += b.RawValue;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator +(FP a, int b)
    {
      a.RawValue += (long) b << 16;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator +(int a, FP b)
    {
      b.RawValue = ((long) a << 16) + b.RawValue;
      return b;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator -(FP a, FP b)
    {
      a.RawValue -= b.RawValue;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator -(FP a, int b)
    {
      a.RawValue -= (long) b << 16;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator -(int a, FP b)
    {
      b.RawValue = ((long) a << 16) - b.RawValue;
      return b;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator *(FP a, FP b)
    {
      a.RawValue = a.RawValue * b.RawValue >> 16;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator *(FP a, int b)
    {
      a.RawValue *= (long) b;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator *(int a, FP b)
    {
      b.RawValue = (long) a * b.RawValue;
      return b;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator /(FP a, FP b)
    {
      a.RawValue = (a.RawValue << 16) / b.RawValue;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator /(FP a, int b)
    {
      a.RawValue /= (long) b;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator /(int a, FP b)
    {
      b.RawValue = ((long) a << 32) / b.RawValue;
      return b;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator /(FP a, FPHighPrecisionDivisor b)
    {
      a.RawValue = FPHighPrecisionDivisor.RawDiv(a.RawValue, b.RawValue);
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator %(FP a, FP b)
    {
      a.RawValue %= b.RawValue;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator %(FP a, int b)
    {
      a.RawValue %= (long) b << 16;
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator %(int a, FP b)
    {
      b.RawValue = ((long) a << 16) % b.RawValue;
      return b;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP operator %(FP a, FPHighPrecisionDivisor b)
    {
      a.RawValue = FPHighPrecisionDivisor.RawMod(a.RawValue, b.RawValue);
      return a;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator <(FP a, FP b) => a.RawValue < b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator <(FP a, int b) => a.RawValue < (long) b << 16;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator <(int a, FP b) => (long) a << 16 < b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator <=(FP a, FP b) => a.RawValue <= b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator <=(FP a, int b) => a.RawValue <= (long) b << 16;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator <=(int a, FP b) => (long) a << 16 <= b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator >(FP a, FP b) => a.RawValue > b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator >(FP a, int b) => a.RawValue > (long) b << 16;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator >(int a, FP b) => (long) a << 16 > b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator >=(FP a, FP b) => a.RawValue >= b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator >=(FP a, int b) => a.RawValue >= (long) b << 16;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator >=(int a, FP b) => (long) a << 16 >= b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(FP a, FP b) => a.RawValue == b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(FP a, int b) => a.RawValue == (long) b << 16;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator ==(int a, FP b) => (long) a << 16 == b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(FP a, FP b) => a.RawValue != b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(FP a, int b) => a.RawValue != (long) b << 16;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool operator !=(int a, FP b) => (long) a << 16 != b.RawValue;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static implicit operator FP(int value)
    {
      FP fp;
      fp.RawValue = (long) value << 16;
      return fp;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static implicit operator FP(uint value)
    {
      FP fp;
      fp.RawValue = (long) value << 16;
      return fp;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static implicit operator FP(short value)
    {
      FP fp;
      fp.RawValue = (long) value << 16;
      return fp;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static implicit operator FP(ushort value)
    {
      FP fp;
      fp.RawValue = (long) value << 16;
      return fp;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static implicit operator FP(sbyte value)
    {
      FP fp;
      fp.RawValue = (long) value << 16;
      return fp;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static implicit operator FP(byte value)
    {
      FP fp;
      fp.RawValue = (long) value << 16;
      return fp;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator int(FP value) => (int) (value.RawValue >> 16);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator long(FP value) => value.RawValue >> 16;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator float(FP value) => (float) value.RawValue / 65536f;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static explicit operator double(FP value) => (double) value.RawValue / 65536.0;

    [Obsolete("Don't cast from float to FP", true)]
    public static implicit operator FP(float value) => throw new InvalidOperationException();

    [Obsolete("Don't cast from double to FP", true)]
    public static implicit operator FP(double value) => throw new InvalidOperationException();

    /// <summary>Returns integral part as long.</summary>
    public long AsLong
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this.RawValue >> 16;
    }

    /// <summary>Return integral part as int.</summary>
    public int AsInt
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => (int) (this.RawValue >> 16);
    }

    /// <summary>Return integral part as int.</summary>
    public short AsShort
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => (short) (this.RawValue >> 16);
    }

    /// <summary>Converts to float.</summary>
    public float AsFloat
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => (float) this.RawValue / 65536f;
    }

    /// <summary>Converts to double.</summary>
    public double AsDouble
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get => (double) this.RawValue / 65536.0;
    }

    public static unsafe void Serialize(void* ptr, IDeterministicFrameSerializer serializer) => serializer.Stream.Serialize(&((FP*) ptr)->RawValue);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    internal FP(long v) => this.RawValue = v;

    public int CompareTo(FP other) => this.RawValue.CompareTo(other.RawValue);

    public bool Equals(FP other) => this.RawValue == other.RawValue;

    public override bool Equals(object obj) => obj is FP fp && this.RawValue == fp.RawValue;

    public override int GetHashCode() => this.RawValue.GetHashCode();

    public override string ToString() => this.AsFloat.ToString((IFormatProvider) CultureInfo.InvariantCulture);

    public string ToString(string format) => this.AsDouble.ToString(format, (IFormatProvider) CultureInfo.InvariantCulture);

    public string ToStringInternal()
    {
      long num = Math.Abs(this.RawValue);
      string str = string.Format("{0}.{1}", (object) (num >> 16), (object) (num % 65536L).ToString((IFormatProvider) CultureInfo.InvariantCulture).PadLeft(5, '0'));
      return this.RawValue < 0L ? "-" + str : str;
    }

    public static FP FromFloat_UNSAFE(float value) => new FP(checked ((long) unchecked ((double) value * 65536.0)));

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP FromRaw(long value)
    {
      FP fp;
      fp.RawValue = value;
      return fp;
    }

    public static FP FromString_UNSAFE(string value) => FP.FromFloat_UNSAFE((float) double.Parse(value, (IFormatProvider) CultureInfo.InvariantCulture));

    public static FP FromString(string value)
    {
      if (value == null)
        return FP._0;
      value = value.Trim();
      if (value.Length == 0)
        return FP._0;
      bool flag1 = value[0] == '.';
      bool flag2;
      if (flag2 = value[0] == '-')
        value = value.Substring(1);
      string[] strArray = value.Split(new char[1]{ '.' }, StringSplitOptions.RemoveEmptyEntries);
      long v;
      switch (strArray.Length)
      {
        case 1:
          v = !flag1 ? FP.ParseInteger(strArray[0]) : FP.ParseFractions(strArray[0]);
          break;
        case 2:
          v = checked (FP.ParseInteger(strArray[0]) + FP.ParseFractions(strArray[1]));
          break;
        default:
          throw new FormatException(value);
      }
      return flag2 ? new FP(-v) : new FP(v);
    }

    private static long ParseInteger(string format) => long.Parse(format) * 65536L;

    private static long ParseFractions(string format)
    {
      long num;
      switch (format.Length)
      {
        case 0:
          return 0;
        case 1:
          num = 10L;
          break;
        case 2:
          num = 100L;
          break;
        case 3:
          num = 1000L;
          break;
        case 4:
          num = 10000L;
          break;
        case 5:
          num = 100000L;
          break;
        case 6:
          num = 1000000L;
          break;
        case 7:
          num = 10000000L;
          break;
        default:
          if (format.Length > 14)
            format = format.Substring(0, 14);
          num = 100000000L;
          for (int index = 8; index < format.Length; ++index)
            num *= 10L;
          break;
      }
      return (long.Parse(format) * 65536L + num / 2L) / num;
    }

    internal static long RawMultiply(FP x, FP y) => x.RawValue * y.RawValue >> 16;

    internal static long RawMultiply(FP x, FP y, FP z)
    {
      y.RawValue = x.RawValue * y.RawValue >> 16;
      return y.RawValue * z.RawValue >> 16;
    }

    internal static long RawMultiply(FP x, FP y, FP z, FP a)
    {
      y.RawValue = x.RawValue * y.RawValue >> 16;
      z.RawValue = y.RawValue * z.RawValue >> 16;
      return z.RawValue * a.RawValue >> 16;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static FP MulTruncate(FP x, FP y) => FP.FromRaw(x.RawValue * y.RawValue >> 16);

    public static class Raw
    {
      public const long SmallestNonZero = 1;
      public const long MinValue = -9223372036854775808;
      public const long MaxValue = 9223372036854775807;
      public const long UseableMin = -2147483648;
      public const long UseableMax = 2147483647;
      public const long Pi = 205887;
      public const long PiInv = 20860;
      public const long PiTimes2 = 411774;
      public const long PiOver2 = 102943;
      public const long PiOver2Inv = 41721;
      public const long PiOver4 = 51471;
      public const long Pi3Over4 = 154415;
      public const long Deg2Rad = 1143;
      public const long Rad2Deg = 3754936;
      public const long _0 = 0;
      public const long _1 = 65536;
      public const long _2 = 131072;
      public const long _3 = 196608;
      public const long _4 = 262144;
      public const long _5 = 327680;
      public const long _6 = 393216;
      public const long _7 = 458752;
      public const long _8 = 524288;
      public const long _9 = 589824;
      public const long _10 = 655360;
      public const long _99 = 6488064;
      public const long _100 = 6553600;
      public const long _200 = 13107200;
      public const long _1000 = 65536000;
      public const long _10000 = 655360000;
      public const long _0_01 = 655;
      public const long _0_02 = 1310;
      public const long _0_10 = 6553;
      public const long _0_20 = 13107;
      public const long _0_25 = 16384;
      public const long _0_50 = 32768;
      public const long _0_75 = 49152;
      public const long _0_03 = 1965;
      public const long _0_04 = 2620;
      public const long _0_05 = 3275;
      public const long _0_33 = 21845;
      public const long _0_99 = 64881;
      public const long Minus_1 = -65536;
      public const long Rad_180 = 205887;
      public const long Rad_90 = 102943;
      public const long Rad_45 = 51471;
      public const long Rad_22_50 = 25735;
      public const long _1_01 = 66191;
      public const long _1_02 = 66846;
      public const long _1_03 = 67502;
      public const long _1_04 = 68157;
      public const long _1_05 = 68812;
      public const long _1_10 = 72089;
      public const long _1_20 = 78643;
      public const long _1_25 = 81920;
      public const long _1_50 = 98304;
      public const long _1_75 = 114688;
      public const long _1_33 = 87381;
      public const long _1_99 = 130417;
      public const long EN1 = 6553;
      public const long EN2 = 655;
      public const long EN3 = 65;
      public const long EN4 = 6;
      public const long EN5 = 0;
      public const long Epsilon = 65;
      public const long E = 178145;
      public const long Log2_E = 94548;
      public const long Log2_10 = 217705;
    }

    public class Comparer : IComparer<FP>
    {
      public static readonly FP.Comparer Instance = new FP.Comparer();

      private Comparer()
      {
      }

      int IComparer<FP>.Compare(FP x, FP y) => x.RawValue.CompareTo(y.RawValue);
    }

    public class EqualityComparer : IEqualityComparer<FP>
    {
      public static readonly FP.EqualityComparer Instance = new FP.EqualityComparer();

      private EqualityComparer()
      {
      }

      bool IEqualityComparer<FP>.Equals(FP x, FP y) => x.RawValue == y.RawValue;

      int IEqualityComparer<FP>.GetHashCode(FP num) => num.RawValue.GetHashCode();
    }
  }
}